package ru.averta.notes.service.search;

import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ru.averta.notes.dao.CommonSubjectDao;
import ru.averta.notes.domain.TagConnector;
import ru.averta.notes.service.security.SecurityService;
import ru.averta.notes.service.tag.TagManager;

import java.util.Collection;

/**
 * Класс фильтрует список Subject'ов на основании различных критериев, используя DAO (Hibernate)
 * @author fedor.belov
 */

@Component
public class SubjectCriteriaSearchDao {

    @Autowired private SecurityService security;
    @Autowired private CommonSubjectDao subjectDao;
    @Autowired private TagManager tagManager;

    public Criteria getHCriteriaBySubjectCriteria(SubjectCriteriaBean subjectCriteria) {
        Criteria criteria = subjectDao.createHibernateCriteria();
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        loadCriteria_currentUser(criteria, subjectCriteria);
        loadCriteria_target(criteria, subjectCriteria);
        loadCriteria_other(criteria, subjectCriteria);

        return criteria;
    }

    private void loadCriteria_currentUser(Criteria criteria, SubjectCriteriaBean subjectCriteria) {
        criteria.add(Restrictions.eq("user", security.getCurrentUser()));
    }

    private void loadCriteria_target(Criteria criteria, SubjectCriteriaBean subjectCriteria) {
        if (subjectCriteria.getTarget() == SubjectCriteriaBean.SubjectFilterTarget.All) {
            //Все ок
        } else if (subjectCriteria.getTarget() == SubjectCriteriaBean.SubjectFilterTarget.Inbox) {
            //TODO
        }

        if (subjectCriteria.getTarget() == SubjectCriteriaBean.SubjectFilterTarget.Archive) {
            criteria.add(Restrictions.eq("archived", true));
        } else {
            criteria.add(Restrictions.eq("archived", false));
        }
    }

    private void loadCriteria_other(Criteria criteria, SubjectCriteriaBean subjectCriteria) {
        if (subjectCriteria.isFilterFavorite()) {
            criteria.add(Restrictions.eq("favorite", true));
        }

        Collection<String> filterTags = subjectCriteria.getFilterTags();
        if (filterTags != null) {
           /* criteria
                    .createAlias("tagConnectors", "tc")
                    .createAlias("tc.tag", "tag")
                    .add(Restrictions.in("tag.title", filterTags)); */


            if (filterTags.size() == 1) {
                String tag = filterTags.iterator().next();
                criteria
                    .createAlias("tagConnectors", "tc")
                    .createAlias("tc.tag", "tag")
                    .add(Restrictions.eq("tag.title", tag));
            } else {

                /* Не очень понятно, но каким-то макаром эта хрень работает =) */
                /* Мои вопросы на форумах по теме:
                 * http://www.sql.ru/forum/actualthread.aspx?tid=848057
                 * http://stackoverflow.com/questions/5851750/how-can-i-filter-related-collection-with-and-condition-hibernate-criteria-api
                 */
                /* Пример корректно работающего запроса
                 * select distinct art.* from Article art, ArticleAndTag at
                 * where at.ArticleId = art.Id
                 * AND (SELECT COUNT(Id) from ArticleAndTag where TagId = 1 and ArticleId = art.Id) > 0
                 * AND (SELECT COUNT(Id) from ArticleAndTag where TagId = 2 and ArticleId = art.Id) > 0
                 */
                for (String tag : filterTags) {
                    DetachedCriteria dc = DetachedCriteria.forClass(TagConnector.class, "tc");
                    dc
                            .createAlias("tag", "tag")
                            .add(Restrictions.eq("tag.title", tag))
                            .add(Restrictions.eqProperty("tc.baseSubject.id", "subject.id"))
                            .setProjection(Projections.id());

                    criteria.add(Subqueries.exists(dc));
                }
            }
        }
    }

}
